package main

import (
	r "ansible-backend/redis_config"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"math/rand"
	"os/exec"
	"time"

	//"os/exec"
	//"time"

	"github.com/garyburd/redigo/redis"
)

const (
	locked_key       string = "get_task_status"
	PlaybookTaskQKEY string = "playbook_tasks_queue"
	PrePareTaskMap   string = "prepare_run_tasks_map"
	PrePareTaskQ     string = "prepare_task_queue"
	PlaybookTaskId   string = "playbooktaskid"
	PlaybookResMap   string = "playbook_execute_results_map"
	PlaybookResQ     string = "playbook_execute_results_queue"
)

/*
func Checkerr(err error) error {
	if err != nil {
		return err
	}
}
*/
type TaskExecResult struct {
	TaskId       string      `json:"taskid"`
	Result       interface{} `json:"result"`
	Status       string      `json:"status"`
	Cmdline      string      `json:"cmdline"`
	ResponseUrl  string      `json:"response_url"`
	ResponseUrl2 string      `json:"response_url2"`
}

type Inv struct {
	All struct {
		Hosts []string `json:"hosts"`
		//Host_vars map[string]string `json:"vars"`

		Vars struct {
			TaskId       string `json:"task_id"`
			TaskInstance string `json:"task_instance"`
			TaskAction   string `json:"task_action"`
			TaskRole     string `json:"task_role"`
			ResponseUrl  string `json:"response_url"`
			ResponseUrl2 string `json:"response_url2"`
		} `json:"vars"`
	} `json:"all"`
}

var Pool *redis.Pool

func Redispool(Redis_Host string, Redis_Pass string) {
	RedisHost := Redis_Host
	fmt.Println(RedisHost)
	option := redis.DialPassword(Redis_Pass)
	Pool = &redis.Pool{
		MaxIdle:     10,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			//return redis.Dial("tcp", RedisHost, option)
			c, err := redis.Dial("tcp", RedisHost, option)
			if nil != err {
				fmt.Println(err)
				//to do alert
				return nil, err
			}
			return c, nil
		},

		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			if time.Since(t) < time.Minute {
				return nil
			}
			_, err := c.Do("PING")
			return err
		},
	}
}

func GetEnvFromCmd() string {
	GetCmdEnv := flag.String("env", "nothing", "default nothing")
	flag.Parse()
	//fmt.Printf("%T\n", GetCmdEnv)
	//fmt.Println(*GetCmdEnv)
	if *GetCmdEnv == "nothing" {
		//fmt.Println("Usage: env\n")
		fmt.Println("Usage: xx.go  -env \"[test|online]\"")
	}
	CmdEnv := *GetCmdEnv
	return CmdEnv
}

func Lpopdata(key string) (data string, err error) {
	c := Pool.Get()
	data, err = redis.String(c.Do("lpop", key))
	if err != nil {
		fmt.Println(err)
	}
	return data, err
}

func Setdata(key string, value interface{}) (err error) {
	c := Pool.Get()
	_, err = c.Do("set", key, value)
	if err != nil {
		return err
	}
	return nil
}

func QueuedKeyCount(key string) (int, error) {
	c := Pool.Get()
	/*
		exist, err := redis.Int(c.Do("exists", key))

		if err != nil || exist < 1 {
			fmt.Println("enter exists key")
			//return 0, errors.New("could not found redis key")
			return 0, err
		}
	*/
	lenqueue, err := redis.Int(c.Do("llen", key))
	if err != nil {
		return 0, err
	}
	return lenqueue, nil

}

func Lpushdata(key string, value string) (err error) {
	c := Pool.Get()
	_, err = c.Do("rpush", key, value)
	if err != nil {
		fmt.Println("rpush data error")
		//return errors.New("lpush data error")
		return err
	}
	return nil
}

func Hsetdata(key string, filed string, value string) (err error) {
	c := Pool.Get()
	_, err = c.Do("hset", key, filed, value)
	if err != nil {
		fmt.Println("hset data error")
		//return errors.New("lpush data error")
		return err
	}
	return nil
}

func hexistsFiled(key string, filed string) (data interface{}, err error) {
	c := Pool.Get()
	data, err = c.Do("hexists", key, filed)
	if err != nil {
		fmt.Println("hexistsFiled data error")
		//return errors.New("lpush data error")
		return 0, err
	}
	return data, nil
}

func hgetfiled(key string, filed string) (data interface{}, err error) {
	c := Pool.Get()
	data, err = redis.String(c.Do("hget", key, filed))
	if err != nil {
		fmt.Println("hget data error")
		//return errors.New("lpush data error")
		return 0, err
	}
	return data, nil
}

func Getdata(key string) (data int, err error) {
	c := Pool.Get()
	data, err = redis.Int(c.Do("get", key))
	if err != nil {
		return 0, err
	}
	return data, err
}

func ExecPlaybookOutPut(cmdStr string) (data interface{}, err error) {
	//fmt.Println(cmdStr)
	cmd := exec.Command("sh", "-c", cmdStr)
	var out bytes.Buffer
	var stderr bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &stderr
	//out, err := cmd.Output()
	err = cmd.Run()
	/*
				if err != nil {
		            fmt.Printf("exec cmd occur error")
					fmt.Println(err)
					#return nil, err
				}
				//fmt.Println(string(out))
				data = string(out)
				return data, nil
	*/
	if err != nil {
		fmt.Printf("exec cmd occur error in execPlaybookoutput func\n")
		//fmt.Println(fmt.Sprint(err) + ":" + stderr.String())
		//return nil, err
	}
	fmt.Printf("enter Stdout\n")
	//fmt.Println("Result:" + out.String())
	return out.String(), nil

}

func ExecPlaybook(cmdStr string) error {
	fmt.Println(cmdStr)
	cmd := exec.Command("sh", "-c", cmdStr)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}

	if err = cmd.Start(); err != nil {
		return err
	}

	for {
		tmp := make([]byte, 1024)
		_, err := stdout.Read(tmp)
		fmt.Print(string(tmp))
		if err != nil {
			break
		}
	}

	if err = cmd.Wait(); err != nil {
		return err
	}
	return err
}

/*
func Get_Locked_Status(key string) (data int, err error) {

}
*/

/*
 func GetRunTicket(locked,c chan int){
	for i:=0;i<=10000;i++ {
		if get runticked == 1{
			key = 1
			c <- key
		else
		    time.sleep(1)
		}
	}
 }
*/
func Get_CMD_RUN() {
	time.Sleep(time.Second * time.Duration(rand.Intn(3)))
	locked, err := Getdata(locked_key)
	if err != nil {
		fmt.Printf("get locked key from redis failed\n")
		panic("get locked key failed")
		fmt.Println(err)
	}

	fmt.Printf("get locked key %v form redis\n", locked)
	if locked == 0 {
		locked = 1
		err := Setdata(locked_key, locked)
		if err != nil {
			fmt.Printf("set data error\n")
			panic("set locked status to 1 failed")
		}

		fmt.Printf("enter loacked and valule:%v\n", locked)
		//Redispool()
		/*
			data, err := Lpopdata("playbook_tasks_queue")

				if err != nil {
					fmt.Println(err)
				}
				fmt.Println("cmd")
				fmt.Println(data)
		*/
		fmt.Printf("enter running playbook status\n")
		time.Sleep(time.Second * 5)
		fmt.Printf("finshed playbook tasks\n")
		locked = 0
		err = Setdata(locked_key, locked)
		if err != nil {
			fmt.Println("set lockedkey to 0 failed\n")
			fmt.Println(err)
			panic("set locked status to 0 failed")
		} else {
			fmt.Printf("enter unloacked and valule:%v\n", locked)
		}

	} else {
		fmt.Printf("pop data has struck in locked status\n")
	}

}

func main() {
	//defer Pool.Close()
	Env := GetEnvFromCmd()
	var R2 r.RedisConfig
	R2 = r.GetRedisConf(Env)
	fmt.Println(R2.FullHost)

	Redispool(R2.FullHost, R2.Passwd)
	defer Pool.Close()

	var ShellCmd string
	var LogSuffix string = " |tee -a /tmp/ansible_execute.log"

	/*
			var locked int
			var key string
			var value int

				key = "locked"
				//value = 0
		     	//err := Setdata(key, value)
				if err != nil {
					fmt.Println(err)
				} else {
					locked = 0
				}
	*/
	/*
		 c: = make(chan int)
		 GetRunTicked(locked_key,c chan int)
		 locked_status := <-c
		 if locked_status == 0 {
			go DoPlaybookTask()
		 }
	*/

	/*
		i := 0
		for i < 20 {
			fmt.Println(i)
			go Get_CMD_RUN()
			//go Get_CMD_RUN(locked)
			i++
		}

		time.Sleep(time.Second * 100)

	*/
	/*
		var ShellCmd string
		ShellCmd = "echo hello ghd"
		err := ExecPlaybook(ShellCmd)
		if err != nil {
			fmt.Println(err)
		}

		fmt.Println("Hello,World!")
	*/

	for {
	GetTask:
		Num, err := QueuedKeyCount(PrePareTaskQ)
		if Num < 1 {
			fmt.Printf("there are not any tasks,sleep 2s and continue to get tasks again\n")
			time.Sleep(time.Second * 2)
			goto GetTask
		}

		var TaskInfo Inv
		TaskID, err := Lpopdata(PrePareTaskQ)
		if err != nil {
			fmt.Println("Error: pop get taskid")
			fmt.Println(err)
		}

		//TaskID = "1111"
		//data, err := redis.String(hgetfiled(PrePareTaskMap, TaskID))
		data, err := redis.String(hgetfiled(PrePareTaskMap, TaskID))

		if data == "" {
			fmt.Println("could not found data")
		}
		//fmt.Println(json.Marshal(data))

		if err := json.Unmarshal([]byte(data), &TaskInfo); err == nil {
			fmt.Println("struct ansible inventory")
			//fmt.Println(TaskInfo.All.Vars.TaskId)
			if TaskInfo.All.Vars.TaskId != "" {
				err = Setdata(PlaybookTaskId, TaskInfo.All.Vars.TaskId)
				if err != nil {
					fmt.Println("Error:could not get taskid to run playbook")
				}
				// to do sth
			}
			fmt.Printf("Task info:\n")
			fmt.Println(TaskInfo.All.Vars.TaskId)
			fmt.Println(TaskInfo.All.Vars.TaskAction)
			fmt.Println(TaskInfo.All.Vars.TaskInstance)
			fmt.Println(TaskInfo.All.Vars.TaskRole)
			/*
				fmt.Println(TaskInfo.ALL.Vars.TaskRole)

			*/
			if TaskInfo.All.Vars.TaskRole == "" {
				fmt.Println("Error: to execute ansilbe-playbook need TaskRole vars")
			} else {
				ShellCmd = "cd /root/ansible_playbook;ansible-playbook -i /root/ansible_playbook/dynamic_inventory.py  deploy_game/" + TaskInfo.All.Vars.TaskRole + ".yaml -vv -e task_instance=" + TaskInfo.All.Vars.TaskInstance + " -e task_action=" + TaskInfo.All.Vars.TaskAction + LogSuffix
				fmt.Println(ShellCmd)
			}
			//var ShellCmd string = "cd /root/ansible_playbook;ansible-playbook -i /root/ansible_playbook/dynamic_inventory.py  deploy_game/" + TaskInfo.All.Vars.TaskRole + ".yaml -vv -e task_instance=\"" + TaskInfo.All.Vars.TaskInstance + "\" -e task_action=\"" + TaskInfo.All.Vars.TaskAction + "\""

			data, err := ExecPlaybookOutPut(ShellCmd)
			//fmt.Printf("%T", data)
			if err != nil {
				fmt.Println("Error:exec cmd occur error\n")
				fmt.Println(err)
			}
			fmt.Printf("exec result\n")
			fmt.Println(data)
			CmdResult := TaskExecResult{}
			fmt.Printf("the cmd of %s has exec successfuly\n", ShellCmd)
			CmdResult.TaskId = TaskInfo.All.Vars.TaskId
			CmdResult.Result = data
			CmdResult.ResponseUrl = TaskInfo.All.Vars.ResponseUrl
			CmdResult.ResponseUrl2 = TaskInfo.All.Vars.ResponseUrl2
			CmdResult.Cmdline = ShellCmd
			//fmt.Println(CmdResult)
			jsonstr, err := json.Marshal(CmdResult)
			if err != nil {
				fmt.Printf("Error: Marshal CmdResult occur error\n")
				fmt.Println(err)
			}
			//fmt.Println(jsonstr)
			err = Hsetdata(PlaybookResMap, TaskInfo.All.Vars.TaskId, string(jsonstr))
			if err != nil {
				fmt.Println("hset playbook result error")
				fmt.Println(err)
			}
			err = Lpushdata(PlaybookResQ, TaskInfo.All.Vars.TaskId)
			if err != nil {
				fmt.Println(err)
				fmt.Println("push runed playbook result id error")
			}
		} else {
			fmt.Println(err)
		}
	}
}
